import json
import time
from decimal import Decimal

from django import http
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction
from django.shortcuts import render, redirect
from django.urls import reverse
from django.utils import timezone
from django.views import View
from django_redis import get_redis_connection
from apps.areas.models import Address
from apps.goods.models import SKU
from apps.orders.models import OrderInfo, OrderGoods
from utils.response_code import RETCODE
from utils.views import LoginRequiredJSONMixin


class OrdersSettlementView(View):
    """结算订单"""
    def get(self, request):
        print("-----------------ordersSettlementView-----------------")
        # 1 判断是否是否登录
        user = request.user
        if not user.is_authenticated:
            # 跳转到登录页面
            return redirect(reverse("user:login"))

        # 2 查询收货地址
        try:
            address = Address.objects.filter(user=request.user, is_deleted=False)
            print(address, "address")

        except Address.DoesNotExist:
            # 如果地址为空，渲染模板时会判断，并跳转到地址编辑页面
            address = None

        # 3 查询商品购物车勾选的商品'
        # 01 连接数据库
        redis_conn = get_redis_connection("carts")
        redis_carts = redis_conn.hgetall("carts_%s" % user.id)  # 商品
        carts_selected = redis_conn.smembers("selected_%s" % user.id)
        print(redis_carts, " {'商品id':'count(商品数量)}'")
        print(carts_selected, "勾选的商品: {'sku_id'}")
        if not redis_carts:
            return render(request, "00.html")
        carts_dict = {}
        for sku_id in carts_selected:
            carts_dict[int(sku_id)] = int(redis_carts[sku_id])  # 选定勾选的商品在放到一个新的字典中
        print(carts_dict, "勾选商品与数量合并")

        # 3 查询商品信息
        # 初始化值
        total_count = 0
        total_amount = Decimal(0.00)

        skus = SKU.objects.filter(id__in=carts_dict.keys())
        print(skus, "-------sku----")
        for sku in skus:
            sku.count = carts_dict[sku.id]  # 每一个商品的数量
            sku.amount = sku.count * sku.price  # 总价

            # 计算总数量和总金额
            total_count += sku.count
            total_amount += sku.count * sku.price

        # 运费
        freight = Decimal("10.00")

        # 4 构造购物车、收货地址数据
        context = {
            "addresses": address,
            "skus": skus,
            "total_count": total_count,
            "total_amount": total_amount,
            "freight": freight,
            "payment_amount": total_amount + freight
        }

        return render(request, "place_order.html", context)


class OrderCommitView(LoginRequiredJSONMixin, View):
    """订单提交"""

    def post(self, request):
        """保存订单信息和订单商品信息"""
        # 获取当前要保存的订单数据
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')

        # 校验参数
        if not all([address_id, pay_method]):
            return http.HttpResponseBadRequest('缺少必传参数')
        # 判断address_id是否合法
        try:
            address = Address.objects.get(id=address_id)
            # 判断这个收货地址是否属于当前登录用户
            if address.user != request.user:
                # 如果不属于当前用户，那么返回错误信息信息
                return http.HttpResponseBadRequest('参数address_id错误')
        except Exception:
            return http.HttpResponseBadRequest('参数address_id错误')
        # 判断pay_method是否合法
        if int(pay_method) not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return http.HttpResponseBadRequest('参数pay_method错误')

        # 获取登录用户
        user = request.user
        # 生成订单编号：年月日时分秒+用户编号
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id

        # 显式的开启一个事务
        with transaction.atomic():
            # 创建事务保存点
            save_id = transaction.savepoint()
            try:

                # 保存订单基本信息 OrderInfo（一）
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal('0'),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM[
                        'ALIPAY'] else
                    OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )

                # 从redis读取购物车中被勾选的商品信息
                redis_conn = get_redis_connection('carts')
                redis_cart = redis_conn.hgetall('carts_%s' % user.id)
                selected = redis_conn.smembers('selected_%s' % user.id)
                carts = {}
                for sku_id in selected:
                    carts[int(sku_id)] = int(redis_cart[sku_id])
                sku_ids = carts.keys()

                # 遍历购物车中被勾选的商品信息
                for sku_id in sku_ids:
                    # ------- 修改了代码-------
                    for sql_loop in range(3):
                        # 查询SKU信息
                        sku = SKU.objects.get(id=sku_id)

                        print("{}--正在下单id={}".format(request.user.username, sku_id))

                        # ------- 修改了代码-------
                        # 读取原始库存
                        origin_stock = sku.stock
                        origin_sales = sku.sales

                        # 判断SKU库存
                        sku_count = carts[sku.id]
                        if sku_count > origin_stock:
                            # 事务回滚
                            transaction.savepoint_rollback(save_id)
                            return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})

                        # 乐观锁更新库存和销量
                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count
                        result = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock,
                                                                                          sales=new_sales)
                        # 如果下单失败，但是库存足够时，继续下单，直到下单成功或者库存不足为止
                        if result == 0:
                            if sql_loop == 2:
                                # 事务回滚
                                transaction.savepoint_rollback(save_id)
                                return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '下单失败2'})
                            else:
                                continue
                        else:
                            break

                    # 保存订单商品信息 OrderGoods（多）
                    OrderGoods.objects.create(
                        order=order,
                        sku=sku,
                        count=sku_count,
                        price=sku.price,
                    )

                    # 保存商品订单中总价和总数量
                    order.total_count += sku_count
                    order.total_amount += sku_count * sku.price

                    # 测试用的，可以删除（注意下方的7是当前我在测试时一个用户的id小于7而另外一个是7，所以才写的7，根据实际编写即可）
                    if request.user.id < 10:
                        for i in range(10):
                            print("用户{}正在延时{}提交中...".format(request.user.username, i + 1))
                            time.sleep(1)

                # 添加邮费和保存订单信息
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '下单失败'})

            # 提交订单成功，显式的提交一次事务
            transaction.savepoint_commit(save_id)

        # 清除购物车中已结算的商品
        pl = redis_conn.pipeline()
        pl.hdel('carts_%s' % user.id, *selected)
        pl.srem('selected_%s' % user.id, *selected)
        pl.execute()

        # 响应提交订单结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '下单成功', 'order_id': order.order_id})


class OrderSuccessView(LoginRequiredMixin, View):
    """订单提交成功"""
    def get(self, request):
        # 1 接受参数
        order_id = request.GET.get("order_id")
        payment_amount = request.GET.get("payment_amount")  # 总价
        pay_method = request.GET.get("pay_method")  # 支付方式

        context = {
            "payment_amount": payment_amount,
            "order_id": order_id,
            "pay_method": pay_method
        }

        return render(request, "order_success.html", context)


class OrderCommentView(LoginRequiredMixin, View):
    """订单商品评价"""

    def get(self, request):
        """展示商品评价页面"""
        # 接收参数
        order_id = request.GET.get('order_id')
        # 校验参数
        try:
            OrderInfo.objects.get(order_id=order_id, user=request.user)
        except OrderInfo.DoesNotExist:
            return http.HttpResponseNotFound('订单不存在')

        # 查询订单中未被评价的商品信息
        uncomment_goods = OrderGoods.objects.filter(order_id=order_id, is_commented=False)
        if not uncomment_goods:
            return http.HttpResponseServerError('订单商品信息出错')

        # 构造待评价商品数据
        uncomment_goods_list = []
        for goods in uncomment_goods:
            uncomment_goods_list.append({
                'order_id': goods.order.order_id,
                'sku_id': goods.sku.id,
                'name': goods.sku.name,
                'price': str(goods.price),
                'default_image_url': goods.sku.default_image,
                'comment': goods.comment,
                'score': goods.score,
                'is_anonymous': str(goods.is_anonymous),
            })

        # 渲染模板
        context = {
            'uncomment_goods_list': uncomment_goods_list
        }
        return render(request, 'order_comment.html', context)

    def post(self, request):
        """评价订单商品"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        order_id = json_dict.get('order_id')
        sku_id = json_dict.get('sku_id')
        score = json_dict.get('score')
        comment = json_dict.get('comment')
        is_anonymous = json_dict.get('is_anonymous')
        # 校验参数
        # 判断是否需要的参数都已经传递
        if not all([order_id, sku_id, score, comment]):
            return http.HttpResponseForbidden('缺少必传参数')
        # 判断这个订单是否是当前用户的
        try:
            OrderInfo.objects.filter(order_id=order_id, user=request.user,
                                     status=OrderInfo.ORDER_STATUS_ENUM['UNCOMMENT'])
        except OrderInfo.DoesNotExist:
            return http.HttpResponseForbidden('参数order_id错误')
        # 判断是否有这个商品
        try:
            order_goods = OrderGoods.objects.get(order_id=order_id, sku_id=sku_id, is_commented=False)
        except OrderGoods.DoesNotExist:
            return http.HttpResponseForbidden('参数sku_id错误')
        # 是否是布尔类型
        if is_anonymous and not isinstance(is_anonymous, bool):
            return http.HttpResponseForbidden('参数is_anonymous错误')

        # 保存订单商品评价数据
        order_goods.comment = comment
        order_goods.score = score
        order_goods.is_anonymous = is_anonymous
        order_goods.is_commented = True
        order_goods.save()

        # 累计评论数据
        order_goods.sku.comments += 1
        order_goods.sku.save()
        order_goods.sku.spu.comments += 1
        order_goods.sku.spu.save()

        # 如果所有订单商品都已评价，则修改订单状态为已完成
        if OrderGoods.objects.filter(order_id=order_id, is_commented=False).count() == 0:
            OrderInfo.objects.filter(order_id=order_id).update(status=OrderInfo.ORDER_STATUS_ENUM['FINISHED'])

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '评价成功'})